﻿using GoodReads.API.Data;
using RestSharp;
using RestSharp.Authenticators;
using RestSharp.Contrib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Windows.Security.Authentication.Web;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage.Streams;

namespace GoodReads.API
{
    public class GoodReads
    {
        private const string API_KEY = "x1o7WGS4UEMFOGRtxIPoJA";
        private const string OAUTH_SECRET = "uFajCIRmYH2F7nEjByYFPz1yi65QC4zMU73zUG5ZM";
        //private const string KEY = "&key=x1o7WGS4UEMFOGRtxIPoJA";
        private const string BASEURL = "http://www.goodreads.com/";
        private const string SEARCH = "search.xml";
        private const string AUTHOR = "author/";
        private const string LIST = "list/";
        private const string SHOW = "show/";
        private const string BOOK = "book/";
        private const string ISBNID = "isbn_to_id/";
        private const string UPDATES = "updates/";
        private const string FRIENDS = "friends.xml";
        private static bool isUserAuthenticated;

        public static string OAuthToken { get; set; }
        public static string OAuthTokenSecret { get; set; }
        public static string OAuthAccessToken { get; set; }
        public static string OAuthAccessTokenSecret { get; set; }
        public static string grUsername { get; set; }

        public RestClient client { get; set; }

        public GoodReads()
        {
            isUserAuthenticated = false;

            //RetrieveCredentials();
        }
        public async void Authenticate()
        {
            var baseUrl = "http://www.goodreads.com";

            client = new RestClient(baseUrl);
            client.Authenticator = OAuth1Authenticator.ForRequestToken(API_KEY, OAUTH_SECRET);
            
            var request = new RestRequest("oauth/request_token", Method.GET);
            var response = await client.ExecuteAsync(request);

            var qs = HttpUtility.ParseQueryString(response.Content);
            if (qs != null && qs.Count > 0)
            {
                OAuthToken = qs["oauth_token"];
                OAuthTokenSecret = qs["oauth_token_secret"];
            }

            string goodreadsURL = "https://www.goodreads.com/oauth/authorize?oauth_token=" + OAuthToken;
            WebAuthenticationResult result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, new Uri(goodreadsURL), WebAuthenticationBroker.GetCurrentApplicationCallbackUri());
            if (result.ResponseStatus == WebAuthenticationStatus.Success)
            {
                client.Authenticator = OAuth1Authenticator.ForAccessToken(API_KEY, OAUTH_SECRET, OAuthToken, OAuthTokenSecret);
                request = new RestRequest("oauth/access_token", Method.GET);
                var response2 = await client.ExecuteAsync(request);

                qs = HttpUtility.ParseQueryString(response2.Content);
                if (qs != null && qs.Count > 0)
                {
                    OAuthAccessToken = qs["oauth_token"];
                    OAuthAccessTokenSecret = qs["oauth_token_secret"];
                }

                //StoreCredentials();
            }
        }
        
        #region Persistance
        /// <summary>
        /// Stores the OAuth data in the Roaming folder
        /// </summary>
        private static void StoreCredentials()
        {
            Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
            Windows.Storage.ApplicationDataCompositeValue composite = new Windows.Storage.ApplicationDataCompositeValue();
            composite["accessToken"] = OAuthAccessToken;
            composite["accessTokenSecret"] = OAuthAccessTokenSecret;
            composite["oauthToken"] = OAuthToken;
            composite["oauthTokenSecret"] = OAuthTokenSecret;
            //composite["username"] = username;
            roamingSettings.Values["accessCompositeSetting"] = composite;
        }
        /// <summary>
        /// Retrieves the OAuth data from the Roaming folder
        /// </summary>
        public static bool RetrieveCredentials()
        {
            Windows.Storage.ApplicationDataContainer roamingSettings = Windows.Storage.ApplicationData.Current.RoamingSettings;
            Windows.Storage.ApplicationDataCompositeValue composite = (Windows.Storage.ApplicationDataCompositeValue)roamingSettings.Values["accessCompositeSetting"];

            if (composite == null)
            {
                isUserAuthenticated = false;
            }
            else
            {
                isUserAuthenticated = true;
                OAuthAccessToken = composite["accessToken"].ToString();
                OAuthAccessTokenSecret = composite["accessTokenSecret"].ToString();
                OAuthToken = composite["oauthToken"].ToString();
                OAuthTokenSecret = composite["oauthTokenSecret"].ToString();
            }
            return isUserAuthenticated;
        }
        #endregion
        /// <summary>
        /// Performs a GoodReads search for the given query
        /// </summary>
        /// <param name="query">string to search for</param>
        /// <returns>List of Work items</returns>
        public async Task<List<Work>> Search(string query)
        {
            string results = await HttpGet(BASEURL + SEARCH + "?q=" + query + "&key" + API_KEY);

            var document = XDocument.Parse(results);
            var items = document.Descendants("work");
            var data = new List<Work>();
            foreach (var item in items)
            {
                var xmlbestbook = item.Descendants("best_book").ToList()[0];
                var xmlauthor = xmlbestbook.Descendants("author").ToList()[0];

                var author = new Author()
                {
                    ID = Int32.Parse(xmlauthor.Element("id").Value),
                    Name = xmlauthor.Element("name").Value,
                };

                var bestbook = new BestBook()
                {
                    ID = Int32.Parse(xmlbestbook.Element("id").Value),
                    ImageURL = xmlbestbook.Element("image_url").Value,
                    SmallImageURL = xmlbestbook.Element("small_image_url").Value,
                    Title = xmlbestbook.Element("title").Value,
                    Authors = new List<Author>() { author }
                };

                data.Add(
                    new Work()
                    {
                        BooksCount = Int32.Parse(item.Element("books_count").Value),
                        ID = Int32.Parse(item.Element("id").Value),
                        OriginalPublicationYear = !String.IsNullOrEmpty(item.Element("original_publication_year").Value) ? Int32.Parse(item.Element("original_publication_year").Value) : -1,
                        OriginalPublicationMonth = !String.IsNullOrEmpty(item.Element("original_publication_month").Value) ? Int32.Parse(item.Element("original_publication_month").Value) : -1,
                        OriginalPublicationDay = !String.IsNullOrEmpty(item.Element("original_publication_day").Value) ? Int32.Parse(item.Element("original_publication_day").Value) : -1,
                        RatingsCount = Int32.Parse(item.Element("ratings_count").Value),
                        ReviewsCount = Int32.Parse(item.Element("text_reviews_count").Value),
                        Averagerating = Single.Parse(item.Element("average_rating").Value),
                        BestBook = bestbook
                    }
                );
            }
            return data;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="authorID"></param>
        public async void AuthorBookList(string authorID)
        {
            string results = await HttpGet(BASEURL + AUTHOR + LIST + authorID + ".xml" + "&key" + API_KEY);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="authorID"></param>
        public async void AuthorInfoShow(string authorID)
        {
            string results = await HttpGet(BASEURL + AUTHOR + SHOW + authorID + ".xml" + "&key" + API_KEY);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="isbn"></param>
        public async void ISBNtoID(string isbn)
        {
            string results = await HttpGet(BASEURL + BOOK + ISBNID + isbn + "&key" + API_KEY);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="filter"></param>
        /// <param name="maxUpdates"></param>
        public async void GetFriendUpdates(string type, string filter, string maxUpdates)
        {
            string url = BASEURL + UPDATES + FRIENDS;// +type + filter + "&max_updates=" + maxUpdates + "&access_token=" + OAuthAccessToken;

            client.Authenticator = OAuth1Authenticator.ForProtectedResource(API_KEY, OAUTH_SECRET, OAuthAccessToken, OAuthAccessTokenSecret);
            var request = new RestRequest(UPDATES + FRIENDS, Method.GET);
            var response = await client.ExecuteAsync(request);

            string results = response.ToString();

            var document = XDocument.Parse(results);
            var items = document.Descendants("update");
            var data = new List<Update>();
            foreach (var item in items)
            {
                var xmlactor = item.Descendants("actor").ToList()[0];
                var actor = new Actor()
                {
                    ID = Int32.Parse(xmlactor.Element("id").Value),
                    Name = xmlactor.Element("name").Value,
                    ImageURL = xmlactor.Element("image_url").Value,
                    Link = xmlactor.Element("link").Value
                };

                var xmlaction = item.Descendants("action").ToList()[0];
                var action = new UpdateAction()
                {
                    type = xmlactor.Attribute("type").Value,
                    Shelf = xmlactor.Element("shelf").Attribute("name").Value
                };

                var xmlobject = item.Descendants("object").ToList()[0];

                //var xmlbestbook = item.Descendants("best_book").ToList()[0];
                //var xmlauthor = xmlbestbook.Descendants("author").ToList()[0];

                var author = new Author()
                {
                    ID = Int32.Parse(xmlauthor.Element("id").Value),
                    Name = xmlauthor.Element("name").Value,
                };

                var bestbook = new BestBook()
                {
                    ID = Int32.Parse(xmlbestbook.Element("id").Value),
                    ImageURL = xmlbestbook.Element("image_url").Value,
                    SmallImageURL = xmlbestbook.Element("small_image_url").Value,
                    Title = xmlbestbook.Element("title").Value,
                    Authors = new List<Author>() { author }
                };

                data.Add(
                    new Work()
                    {
                        BooksCount = Int32.Parse(item.Element("books_count").Value),
                        ID = Int32.Parse(item.Element("id").Value),
                        OriginalPublicationYear = !String.IsNullOrEmpty(item.Element("original_publication_year").Value) ? Int32.Parse(item.Element("original_publication_year").Value) : -1,
                        OriginalPublicationMonth = !String.IsNullOrEmpty(item.Element("original_publication_month").Value) ? Int32.Parse(item.Element("original_publication_month").Value) : -1,
                        OriginalPublicationDay = !String.IsNullOrEmpty(item.Element("original_publication_day").Value) ? Int32.Parse(item.Element("original_publication_day").Value) : -1,
                        RatingsCount = Int32.Parse(item.Element("ratings_count").Value),
                        ReviewsCount = Int32.Parse(item.Element("text_reviews_count").Value),
                        Averagerating = Single.Parse(item.Element("average_rating").Value),
                        BestBook = bestbook
                    }
                );
            }
        }

        /// <summary>
        /// Performs an HTTP GET request to the given URL and returns the result.
        /// </summary>
        /// <param name="url">Target URL.</param>
        /// <returns>Text returned by the response.</returns>
        public async static Task<string> HttpGet(string url)
        {
            string httpResponse = null;

            HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(url);
            Request.Method = "GET";
            Request.ContentType = "application/x-www-form-urlencoded";

            try
            {
                HttpWebResponse response = (HttpWebResponse)await Request.GetResponseAsync();
                if (response != null)
                {
                    StreamReader data = new StreamReader(response.GetResponseStream());
                    httpResponse = await data.ReadToEndAsync();
                }
            }
            catch (WebException e) { }
            return httpResponse;
        }

    }
}

      //#region Authentication

      //  public async static Task GetAuthTokens()
      //  {
      //      string baseUrlForRequestToken = "http://www.goodreads.com/oauth/request_token";
      //      //Get basic parameters 
      //      SortedDictionary<string, string> parameters = GetOAuthParameters(API_KEY, OAUTH_SECRET);
      //      //Get signed url 
      //      string signedUrl = CalculateOAuthSignedUrl(parameters, baseUrlForRequestToken, OAUTH_SECRET, false);
      //      //send the signed url to goodreads 
      //      string response = await HttpGet(signedUrl);
      //      SetRequestToken(response);
      //      //return "https://www.goodreads.com/oauth/authorize?oauth_token=" + OAuth_token + "&perms=write"; 
      //  }
      //  public static async Task GetAccessToken(string responseData) 
      //  { 
      //      string oauth_token = null; 
      //      String[] keyValPairs = responseData.Split('&');
      //      string baseUrl = "http://www.goodreads.com/oauth/access_token"; 
      //      //parses the response string 
      //      for (int i = 0; i < keyValPairs.Length; i++) 
      //      { 
      //          String[] split = keyValPairs[i].Split('=');
      //          if (split[0].Equals("oauth_token"))
      //          {
      //              OAuthToken = split[1];
      //          } 
      //          else if (split[0].Equals("oauth_token_secret"))
      //          {
      //              OAuthTokenSecret = split[1];
      //          } 

      //      }

      //      //Get basic parameters 
      //      SortedDictionary<string, string> parameters = GetOAuthParameters(API_KEY, OAUTH_SECRET);
      //      parameters.Add("oauth_token", OAuthToken);
      //      string signedUrl = CalculateOAuthSignedUrl(parameters, baseUrl, OAUTH_SECRET, true);
      //      string response = await HttpGet(signedUrl);
      //      CalculateAccessToken(response);
      //  }
      //  public static void CalculateAccessToken(string responseData)
      //  {
      //      String accessToken = null;
      //      String accessTokenSecret = null;
      //      String[] keyValPairs = responseData.Split('&');
      //      string username = string.Empty;
      //      for (int i = 0; i < keyValPairs.Length; i++)
      //      {
      //          String[] splits = keyValPairs[i].Split('=');
      //          switch (splits[0])
      //          {
      //              case "oauth_token":
      //                  accessToken = splits[1];
      //                  break;
      //              case "oauth_token_secret":
      //                  accessTokenSecret = splits[1];
      //                  break;
      //          }
      //      }
      //      // Set current access token. 
      //      OAuthAccessToken = accessToken;
      //      OAuthAccessTokenSecret = accessTokenSecret;

      //      StoreCredentials();
      //  }
      //  public static SortedDictionary<string, string> GetOAuthParameters(string apikey, string secret)
      //  {
      //      Random random = new Random();
      //      DateTime date = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
      //      TimeSpan timespan = DateTime.UtcNow - date;
      //      string oauthTimestamp = timespan.TotalSeconds.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
      //      string oauthNonce = random.Next(1000).ToString();

      //      var parameters = new SortedDictionary<string, string>();
      //      parameters.Add("oauth_nonce", oauthNonce);
      //      parameters.Add("oauth_timestamp", oauthTimestamp);
      //      parameters.Add("oauth_consumer_key", apikey);
      //      parameters.Add("oauth_signature_method", "HMAC-SHA1");
      //      parameters.Add("oauth_version", "1.0");

      //      return parameters;
      //  }
      //  /// <summary>
      //  /// Signs the OAuth request with HMAC_SHA1
      //  /// </summary>
      //  /// <param name="parameters"></param>
      //  /// <param name="url"></param>
      //  /// <param name="secret"></param>
      //  /// <param name="toggle"></param>
      //  /// <returns></returns>
      //  public static string CalculateOAuthSignedUrl(SortedDictionary<string, string> parameters, string url, string secret, bool toggle)
      //  {
      //      StringBuilder baseString = new StringBuilder();
      //      string str;
      //      IBuffer keyMaterial;

      //      foreach (var param in parameters)
      //      {
      //          baseString.Append(param.Key);
      //          baseString.Append("=");
      //          baseString.Append(Uri.EscapeDataString(param.Value));
      //          baseString.Append("&");
      //      }

      //      //removing the extra ampersand 
      //      baseString.Remove(baseString.Length - 1, 1);
      //      str = "GET&" + Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(baseString.ToString());

      //      //calculating the signature 
      //      MacAlgorithmProvider HmacSha1Provider = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");

      //      if (toggle)
      //      {
      //          keyMaterial = CryptographicBuffer.ConvertStringToBinary(secret + "&" + OAuthTokenSecret, BinaryStringEncoding.Utf8);
      //      }
      //      else
      //      {
      //          keyMaterial = CryptographicBuffer.ConvertStringToBinary(secret + "&", BinaryStringEncoding.Utf8);
      //      }

      //      CryptographicKey cryptoKey = HmacSha1Provider.CreateKey(keyMaterial);
      //      IBuffer dataString = CryptographicBuffer.ConvertStringToBinary(str, BinaryStringEncoding.Utf8);

      //      return url + "?" + baseString.ToString() + "&oauth_signature=" + Uri.EscapeDataString(CryptographicBuffer.EncodeToBase64String(CryptographicEngine.Sign(cryptoKey, dataString)));
      //  }

      //  /// <summary>
      //  /// Extracts the OAuth Token and OAuth Token Secret from the HttpResponse
      //  /// </summary>
      //  /// <param name="response"></param>
      //  private static void SetRequestToken(string response)
      //  {
      //      string[] keyValPairs = response.Split('&');
      //      for (int i = 0; i < keyValPairs.Length; i++)
      //      {
      //          String[] splits = keyValPairs[i].Split('=');
      //          switch (splits[0])
      //          {
      //              case "oauth_token":
      //                  {
      //                      OAuthToken = splits[1];
      //                      break;
      //                  }
      //              case "oauth_token_secret":
      //                  {
      //                      OAuthTokenSecret = splits[1];
      //                      break;
      //                  }
      //          }
      //      }
      //  }
      //  #endregion